#include "dicomviewer.h"
#include "ui_dicomviewer.h"

#include "../MainFrame/aboutdialog.h"
#include "thumbnailbarwidget.h"
#include "viewcontainerwidget.h"
#include <QScrollArea>
#include <QHBoxLayout>
#include <QResizeEvent>
#include <QStyle>
#include <QIcon>
#include <QMenu>
#include <QSettings>
#include <QDir>
#include <QFileDialog>
#include <QScrollBar>
#include "../Global/commondefs.h"
#include "gridpopwidget.h"
#include "../Network/ftpsetupdialog.h"
#include "../Network/ftpclientthread.h"
#include <QThread>
#include <QLocalServer>
#include <QLocalSocket>
#include <QEventLoop>
#include <QKeyEvent>

#define OPEN_DIR_PATH "OPENDIRPATH"
#define OPEN_FILE_PATH "OPENFILEPATH"

DicomViewer::DicomViewer(QWidget *parent) :
    server(new QLocalServer(this)),
    QWidget(parent),
    ui(new Ui::DicomViewer)
{
    ui->setupUi(this);
    //setWindowFlags(windowFlags() | Qt::FramelessWindowHint);
    setupToolBar();
    setupConnection();
    startLocalServer();
    clearImageCache();

    ui->thumbnailBar->installEventFilter(this);
    ui->viewContainer->setEventFilter(this);
}

DicomViewer::~DicomViewer()
{
    delete ui;
}

void DicomViewer::setupToolBar()
{
    QMenu *m;
    QAction *a;

    m = new QMenu(this);
    m->addAction(tr("Open DICOM folder"), this, SLOT(openDicomFolder()));
    m->addAction(tr("Open DICOM file"), this, SLOT(openDicomFile()));
    m->addAction(tr("Open zip file"), this, SLOT(openZipFile()));
    m->addAction(tr("Close all"), ui->thumbnailBar, SLOT(clear()));
    m->addAction(tr("Quit"), this, SLOT(close()));
    ui->fileBtn->setMenu(m);
    m = new QMenu(this);
    m->addAction(tr("DICOM setup"), this, SLOT(dicomSetup()));
    m->addAction(tr("FTP setup"), this, SLOT(ftpSetup()));
    ui->downLoadBtn->setMenu(m);
    m = new QMenu(this);
    m->addAction(tr("Export images"), ui->viewContainer, SLOT(exportImages()));
    m->addAction(tr("Copy to clipboard"), ui->viewContainer, SLOT(copyToClipboard()));
    ui->exportBtn->setMenu(m);
    m = new QMenu(this);
    a = m->addAction(tr("Preview bar"), ui->scrollArea, SLOT(setVisible(bool)));
    a->setCheckable(true);
    a->setChecked(true);
    ui->gridBtn->setMenu(m);
    m = new QMenu(this);
    //a = m->addAction(tr("Annotations"), ui->viewContainer, SLOT(showAnnotations(bool)));
    a = m->addAction(tr("Annotations"));
    a->setCheckable(true);
    a->setChecked(true);
    a = m->addAction(tr("Measurements"), ui->viewContainer, SLOT(showMeasurements(bool)));
    a->setCheckable(true);
    a->setChecked(true);
    a = m->addAction(tr("Cross reference line"), ui->viewContainer, SLOT(showCrossRefLine(bool)));
    a->setCheckable(true);
    a->setChecked(true);
    m->addSeparator();
    m->addAction(tr("Increase font"), ui->viewContainer, SLOT(incAnnoFont()));
    m->addAction(tr("Decrease font"), ui->viewContainer, SLOT(decAnnoFont()));
    m->addAction(tr("Select font"), ui->viewContainer, SLOT(setAnnoFont()));
    m->addSeparator();
    m->addAction(tr("DICOM tags"), ui->viewContainer, SLOT(showDicomTags()));
    ui->annoBtn->setMenu(m);
    m = new QMenu(this);
    m->addAction(tr("Default"), ui->viewContainer, SLOT(setDefaultWL()));
    m->addAction(tr("Full dynamic"), ui->viewContainer, SLOT(setFullDynamic()));
    m->addSeparator();
    m->addAction(QIcon(":/png/contrast.png"), tr("Negative"), ui->viewContainer, SLOT(invert()));
    ui->adjustBtn->setMenu(m);
    m = new QMenu(this);
    m->addAction(tr("Fill viewport"), ui->viewContainer, SLOT(setFillViewport()));
    m->addSeparator();
    m->addAction(tr("100%"), ui->viewContainer, SLOT(setZoom100()));
    m->addAction(tr("200%"), ui->viewContainer, SLOT(setZoom200()));
    m->addAction(tr("400%"), ui->viewContainer, SLOT(setZoom400()));
    m->addSeparator();
    m->addAction(QIcon(":/png/zoomin.png"), tr("Zoom in"), ui->viewContainer, SLOT(setZoomin()));
    m->addAction(QIcon(":/png/zoomout.png"), tr("Zoom out"), ui->viewContainer, SLOT(setZoomout()));
    m->addSeparator();
    m->addAction(QIcon(":/png/magnifier.png"), tr("Magnifier"), ui->viewContainer, SLOT(magnifier()));
    ui->zoomBtn->setMenu(m);
    m = new QMenu(this);
    m->addAction(QIcon(":/png/reset.png"), tr("Reset"), ui->viewContainer, SLOT(resetPan()));
    ui->panBtn->setMenu(m);
    m = new QMenu(this);
    m->addAction(QIcon(":/png/line.png"), tr("Length"), this, SLOT(drawLength()));
    m->addAction(QIcon(":/png/ellipse.png"), tr("Ellipse"), this, SLOT(drawEllipse()));
    m->addAction(QIcon(":/png/rect.png"), tr("Rectangle"), this, SLOT(drawRectangle()));
    m->addAction(QIcon(":/png/angle.png"), tr("Angle"), this, SLOT(drawAngle()));
    m->addAction(QIcon(":/png/text.png"), tr("Text"), this, SLOT(drawText()));
    m->addAction(QIcon(":/png/pencil.png"), tr("Pencil"), this, SLOT(drawPencil()));
    m->addSeparator();
    m->addAction(QIcon(":/png/recycle.png"), tr("Remove all"), ui->viewContainer, SLOT(rmAllMeasurements()));
    ui->marksBtn->setMenu(m);
    m = new QMenu(this);
    m->addAction(QIcon(":/png/rrotate.png"), tr("Rotate CW"), ui->viewContainer, SLOT(rotateCW()));
    m->addAction(QIcon(":/png/lrotate.png"), tr("Rotate CCW"), ui->viewContainer, SLOT(rotateCCW()));
    m->addSeparator();
    m->addAction(QIcon(":/png/reset.png"), tr("Reset"), ui->viewContainer, SLOT(clearRotate()));
    ui->rotateBtn->setMenu(m);
    m = new QMenu(this);
    m->addAction(QIcon(":/png/hflip.png"), tr("Flip horizontal"), ui->viewContainer, SLOT(hFlip()));
    m->addAction(QIcon(":/png/vflip.png"), tr("Flip vertical"), ui->viewContainer, SLOT(vFlip()));
    m->addSeparator();
    m->addAction(QIcon(":/png/reset.png"), tr("Reset"), ui->viewContainer, SLOT(clearFlip()));
    ui->flipBtn->setMenu(m);
    m = new QMenu(this);
    m->addAction(tr("About"), this, SLOT(aboutThis()));
    ui->helpBtn->setMenu(m);
}

void DicomViewer::setupConnection()
{
    connect(ui->thumbnailBar, SIGNAL(seriesInserted(SeriesInstance*)),
            ui->viewContainer, SLOT(onSeriesInserted(SeriesInstance*)));
    connect(ui->thumbnailBar, SIGNAL(imageDoubleClicked(SeriesInstance*)),
            ui->viewContainer, SLOT(onImageDClicked(SeriesInstance*)));

    connect(ui->fileBtn, SIGNAL(clicked(bool)), this, SLOT(openDicomFolder()));
    connect(ui->downLoadBtn, SIGNAL(clicked(bool)), this, SLOT(downloadFromPacs()));
    connect(ui->exportBtn, SIGNAL(clicked(bool)), ui->viewContainer, SLOT(exportImages()));
    connect(ui->gridBtn, SIGNAL(clicked(bool)), this, SLOT(splitView()));
    QAction *a = ui->annoBtn->menu()->actions().first();
    connect(ui->annoBtn, SIGNAL(clicked()), a, SLOT(toggle()));
    connect(a, SIGNAL(toggled(bool)), ui->viewContainer, SLOT(showAnnotations(bool)));
    connect(ui->adjustBtn, SIGNAL(clicked(bool)), ui->viewContainer, SLOT(adjustWL()));
    connect(ui->panBtn, SIGNAL(clicked(bool)), ui->viewContainer, SLOT(panImage()));
    connect(ui->zoomBtn, SIGNAL(clicked(bool)), ui->viewContainer, SLOT(zoomImage()));
    connect(ui->slicingBtn, SIGNAL(clicked(bool)), ui->viewContainer, SLOT(slicing()));
    connect(ui->marksBtn, SIGNAL(clicked(bool)), ui->viewContainer, SLOT(drawLength()));
    connect(ui->flipBtn, SIGNAL(clicked(bool)), ui->viewContainer, SLOT(hFlip()));
    connect(ui->rotateBtn, SIGNAL(clicked(bool)), ui->viewContainer, SLOT(rotateCW()));
    connect(ui->helpBtn, SIGNAL(clicked(bool)), this, SLOT(aboutThis()));

    connect(server, SIGNAL(newConnection()), this, SLOT(onNewConnection()));
}

bool DicomViewer::isServerListening() const
{
    return server->isListening();
}

void DicomViewer::startLocalServer()
{
    QStringList args = QApplication::arguments();
    args.removeFirst();
    QLocalSocket *s = new QLocalSocket;
    connect(s, SIGNAL(disconnected()), s, SLOT(deleteLater()));
    s->connectToServer(QApplication::applicationName());
    if (s->waitForConnected(500)) {
        for (int i = 0; i < args.size(); ++i) {
            s->write(args.at(i).toLocal8Bit());
            s->write(" ");
        }
        s->flush();
        s->waitForBytesWritten();
        QEventLoop l;
        connect(s, SIGNAL(disconnected()), &l, SLOT(quit()));
        l.exec();
    } else {
        server->listen(QApplication::applicationName());
        QStringList dcms;
        foreach (const QString &a, args) {
            dcms += a.split(";", QString::SkipEmptyParts);
        }
        if (dcms.size()) downloadFromFtp(dcms);
    }
}

void DicomViewer::clearImageCache()
{
    QDir d(QApplication::applicationDirPath());
    d.mkpath(IMAGE_CACHE_DIR);
    d.cd(IMAGE_CACHE_DIR);
    QStringList fs = d.entryList(QDir::Files);
    foreach (const QString &f, fs) {
        QFile::remove(d.absolutePath() + QDir::separator() + f);
    }
    ui->thumbnailBar->setFileWatcher(d.absolutePath());
}

void DicomViewer::resizeEvent(QResizeEvent *e)
{
    QWidget::resizeEvent(e);
    QSize s = size();
    if (s.height() < s.width()) {
        ui->scrollArea->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
        ui->boxLayout->setDirection(QBoxLayout::LeftToRight);
        ui->scrollArea->setMinimumSize(IMAGE_LABEL_SIZE + 40, 0);
        ui->thumbnailBar->setDirection(QBoxLayout::TopToBottom);
    } else {
        ui->scrollArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
        ui->boxLayout->setDirection(QBoxLayout::BottomToTop);
        ui->scrollArea->setMinimumSize(0, IMAGE_LABEL_SIZE + 40);
        ui->thumbnailBar->setDirection(QBoxLayout::LeftToRight);
    }
}

bool DicomViewer::eventFilter(QObject *watched, QEvent *event)
{
    if (event->type() == QEvent::KeyPress) {
          QKeyEvent *e = static_cast<QKeyEvent *>(event);
          switch (e->key()) {
          case Qt::Key_Return:
              ui->thumbnailBar->currSeries();
              break;
          case Qt::Key_Home:
              ui->thumbnailBar->firstSeries();
              break;
          case Qt::Key_Right:
              ui->thumbnailBar->nextSeries();
              break;
          case Qt::Key_End:
              ui->thumbnailBar->lastSeries();
              break;
          case Qt::Key_Left:
              ui->thumbnailBar->prevSeries();
              break;
          case Qt::Key_PageUp:
              ui->viewContainer->prevView();
              break;
          case Qt::Key_Up:
              ui->viewContainer->prevFrame();
              break;
          case Qt::Key_PageDown:
              ui->viewContainer->nextView();
              break;
          case Qt::Key_Down:
              ui->viewContainer->nextFrame();
              break;
          default:
              return QObject::eventFilter(watched, event);
          }
          return true;
      } else {
          // standard event processing
          return QObject::eventFilter(watched, event);
      }
}

void DicomViewer::openDicomFolder()
{
    QSettings s;
    QString p = s.value(OPEN_DIR_PATH, ".").toString();
    p = QFileDialog::getExistingDirectory(this, tr("Open dicom directory"), p);
    if (!p.isEmpty()) {
        s.setValue(OPEN_DIR_PATH, p);
        ui->thumbnailBar->setImagePaths(QStringList()<<p);
    }
}

void DicomViewer::openDicomFile()
{
    QSettings s;
    QString p = s.value(OPEN_FILE_PATH).toString();
    QStringList fs = QFileDialog::getOpenFileNames(this, tr("Open dicom files"), p);
    if (!fs.isEmpty()) {
        s.setValue(OPEN_DIR_PATH, fs.first());
        ui->thumbnailBar->setImagePaths(fs);
    }
}

void DicomViewer::openZipFile()
{

}

void DicomViewer::dicomSetup()
{

}

void DicomViewer::ftpSetup()
{
    FtpSetupDialog d(this);
    d.exec();
}

void DicomViewer::downloadFromPacs()
{

}

void DicomViewer::downloadFromFtp(const QStringList &dcms)
{
    clearImageCache();
    FtpClientThread *ftp = new FtpClientThread(dcms, QApplication::applicationDirPath() + QDir::separator() + IMAGE_CACHE_DIR);
    connect(ftp, SIGNAL(finished()), ftp, SLOT(deleteLater()));
    ftp->start();
}

void DicomViewer::onNewConnection()
{
    QLocalSocket *s = server->nextPendingConnection();
    if (s && s->waitForReadyRead()) {
        QByteArray b = s->readAll();
        QStringList args = QString::fromLocal8Bit(b).split(" ", QString::SkipEmptyParts);
        QStringList dcms;
        foreach (const QString &a, args) {
            dcms += a.split(";", QString::SkipEmptyParts);
        }
        if (dcms.size()) downloadFromFtp(dcms);
    }
    delete s;
}

void DicomViewer::splitView()
{
    /*
    // auto delete while hidden
    GridPopWidget *gpw = new GridPopWidget(this);
    connect(gpw, SIGNAL(viewLayout(int,int)), ui->viewContainer, SLOT(setViewLayout(int,int)));
    QRect geo = geometry();
    QRect gridGeo = ui->gridBtn->geometry();
    gpw->move(geo.topLeft() + gridGeo.bottomLeft());
    gpw->show();
    */
    // auto delete while hidden
    GridPopWidget *gpw = new GridPopWidget(ui->gridBtn);
    connect(gpw, SIGNAL(viewLayout(int,int)), ui->viewContainer, SLOT(setViewLayout(int,int)));
    QRect geo = geometry();
    QRect gridGeo = ui->gridBtn->geometry();
    gpw->move(geo.topLeft() + gridGeo.bottomLeft());
    gpw->show();
}

void DicomViewer::drawLength()
{
    ui->marksBtn->setIcon(QIcon(":/png/line.png"));
    ui->viewContainer->drawLength();
}

void DicomViewer::drawAngle()
{
    ui->marksBtn->setIcon(QIcon(":/png/angle.png"));
    ui->viewContainer->drawAngle();
}

void DicomViewer::drawEllipse()
{
    ui->marksBtn->setIcon(QIcon(":/png/ellipse.png"));
    ui->viewContainer->drawEllipse();
}

void DicomViewer::drawRectangle()
{
    ui->marksBtn->setIcon(QIcon(":/png/rect.png"));
    ui->viewContainer->drawRectangle();
}

void DicomViewer::drawText()
{
    ui->marksBtn->setIcon(QIcon(":/png/text.png"));
    ui->viewContainer->drawText();
}

void DicomViewer::drawPencil()
{
    ui->marksBtn->setIcon(QIcon(":/png/pencil.png"));
    //ui->viewContainer->drawPencil();
}

void DicomViewer::on_fullScreenBtn_clicked(bool checked)
{
    if (checked) {
        QRect r = geometry();
        QSettings().setValue("WindowGeometry", r);
        setWindowState(Qt::WindowFullScreen);
        ui->fullScreenBtn->setIcon(QIcon(":/png/full_screen_exit.png"));
    } else {
        QRect r = QSettings().value("WindowGeometry").toRect();
        setWindowState(Qt::WindowNoState);
        if (r.isValid()) this->setGeometry(r);
        ui->fullScreenBtn->setIcon(QIcon(":/png/full_screen.png"));
    }
}

void DicomViewer::aboutThis()
{
    AboutDialog d(this);
    d.exec();
}
